package com.study.springbootsecurity.entity.business;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;

import java.util.Collection;

/**
 * 用于短信验证码认证的令牌类，继承自 Spring Security 的 AbstractAuthenticationToken。
 * 该类封装了手机号认证所需的信息，支持未认证和已认证两种状态。
 * @author: Huangjianyun
 * @date: 2025-06-14 16:06
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    // 存储用户的手机号，作为认证的主体
    private final Object principal;
    private Object credentials;

    /**
     * 构建一个未经过认证的 SmsAuthenticationToken 实例。
     * 此构造函数创建的令牌处于未认证状态，需要通过认证流程进行验证。
     *
     * @param principal 用户手机号，作为认证的主体
     */
    public SmsCodeAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }
    /**
     * 构建一个已通过认证的 SmsAuthenticationToken 实例。
     * 此构造函数创建的令牌处于已认证状态，包含用户的权限集合。
     *
     * @param principal   用户手机号，作为认证的主体
     * @param authorities 用户拥有的权限集合
     */
    public SmsCodeAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }
    /**
     * 静态工厂方法，创建一个未认证的 SmsAuthenticationToken 实例。
     * 语法糖方法，用于更清晰地表达创建未认证令牌的意图。
     *
     * @param principal 用户手机号
     * @return 未认证的 SmsAuthenticationToken 实例
     */
    public static SmsCodeAuthenticationToken unauthenticated(Object principal, Object credentials) {
        return new SmsCodeAuthenticationToken(principal, credentials);
    }
    /**
     * 静态工厂方法，创建一个已认证的 SmsAuthenticationToken 实例。
     * 语法糖方法，用于更清晰地表达创建已认证令牌的意图。
     *
     * @param principal   用户手机号
     * @param authorities 用户权限集合
     * @return 已认证的 SmsAuthenticationToken 实例
     */
    public static SmsCodeAuthenticationToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        return new SmsCodeAuthenticationToken(principal, credentials, authorities);
    }
    /**
     * 获取认证主体（用户手机号）。
     *
     * @return 用户手机号
     */
    public Object getCredentials() {
        return this.credentials;
    }
    /**
     * 获取（手机验证码）。
     *
     * @return 手机验证码
     */
    public Object getPrincipal() {
        return this.principal;
    }
    /**
     * 设置认证状态。
     * 重写此方法以防止通过常规方式将令牌设置为已认证状态，
     * 强制要求使用带权限集合的构造函数创建已认证令牌，确保安全性。
     *
     * @param isAuthenticated 是否认证（必须为 false）
     * @throws IllegalArgumentException 如果尝试将令牌设置为已认证状态
     */
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        super.setAuthenticated(false);
    }
    /**
     * 清除敏感信息（如凭证）。
     * 由于本实现中不使用凭证，此方法仅调用父类的相应方法。
     */
    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }
}
